package com.fpt.mockproject.j21.absd.dao;

import java.io.Serializable;
import java.util.List;

import javax.persistence.Column;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.annotations.NaturalId;

/**
 * HIBERNATE SIMPLIZER VERSION 1.2
 * This is primary class used to create a hibernate simplizer to used.
 * 
 * E : The entity Object Class (Account, Employee, ...)
 * S : The datatype of primary key (String, Integer, Long, ...)
 * 
 * Hibernate Version required : 4.3.4
 * 
 * @param Class<?> annotatedClass : inject the 'E'.class (Account.class, Employee.class, ...)
 * 
 * REMEMBER : DONT FORGET EXECUTE .close() WHEN ENDING USING HIBERNATE SIMPLIZER
 * 
 * @author Linh HB Breaker
 * @version 1.2
 * @since 27/06/2014
 */
public class HibernateSimplizer<E, S> extends HibernateSimplizerCofigAndInit implements HibernateSimplizerInterface<E, S>
{
	/**
	 * Contructor with annotated Class parameter
	 * @param annotatedClass
	 * @return void
	 */
	public HibernateSimplizer(Class<?> annotatedClass) {
		super(annotatedClass);
	}
	
	/**
	 * Contructor with annotated Class parameter
	 * @param annotatedClass
	 * @return void
	 */
	public HibernateSimplizer() {
		super();
	}

	/**
	 * Use to save (insert) an object to database
	 * @param E e
	 * @return boolean
	 */
	@Override
	public boolean save(E e) {
		  Transaction tx = null;
		  try{
		     tx = session.beginTransaction();
		     session.save(e); 
		     tx.commit();
		  }catch (HibernateException ex) {
		     if (tx!=null) tx.rollback();
		     return false;
		  }catch(Exception ex1)
		  {
			 return false;
		  }
		return true;
	}

	/**
	 * Use to delete an object by primary key S s
	 * @param S s
	 * @return boolean
	 */
	@Override
	public boolean delete(S s) {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			E e = (E)session.get(this.annotatedClass, (Serializable) s);
			session.delete(e);
			tx.commit();
		} catch (HibernateException ex) {
			if (tx!=null) tx.rollback();
		    return false;
		}catch(Exception ex1)
		{
			return false;
		}
		return true;
	}
	
	/**
	 * Use to delete an object by any naturalID
	 * 
	 * EXAMPLE :
	 *    I want to delete an Employee by hjz/her employeeName (unique, not PK)
	 *       @NaturalId(mutable = false)
	 *       @Column(name = "???", unique = true, nullable = false)
	 *       private String employeeName;
	 *       
	 *   => JUST USE deleteByNaturalId("xxxx");
	 *   "xxxx" is a string name of employeeName in database
	 * 
	 * @param S s
	 * @return boolean
	 */
	@Override
	public boolean deleteByNaturalId(Object object) {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			E e = (E)session.bySimpleNaturalId(this.annotatedClass).load((Serializable) object);
			session.delete(e);
			tx.commit();
		} catch (HibernateException ex) {
			if (tx!=null) tx.rollback();
		    return false;
		}catch(Exception ex1)
		{
			return false;
		}
		return true;
	}
	
	/**
	 * Use to update an object by Entity Object
	 * 
	 * EXAMPLE : 
	 * 		Employee e = hibernateSimplizer.get(?);
	 * 		e.setName("new name");
	 * 		e.setAge(new age);
	 * 		hibernateSimplizer.update(e);
	 * 
	 * @param E e
	 * @return boolean
	 */
	@Override
	public boolean update(E e) {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			session.update(e);
			tx.commit();
		} catch (HibernateException ex) {
			if (tx!=null) tx.rollback();
		    return false;
		}catch(Exception ex1)
		{
			return false;
		}
		return true;
	}
	
	/**
	 * Use to save or update an object by Entity Object
	 * 
	 * JUST USE THIS WHEN U WANT TO INSERT AN OBJECT IF IT IS NOT EXISTED IN DATABASE
	 * OR IF IT IS EXISTED THEN UPDATE IT.
	 * 
	 * @param E e
	 * @return boolean
	 */
	@Override
	public boolean saveOrUpdate(E e) {
		Transaction tx = null;
		try {
			tx = session.beginTransaction();
			session.saveOrUpdate(e);
			tx.commit();
		} catch (HibernateException ex) {
			if (tx!=null) tx.rollback();
		    return false;
		}catch(Exception ex1)
		{
			return false;
		}
		return true;
	}
	
	/**
	 * Use to GET AN OBJECT BY PRIMARY KEY FROM DATABASE AND AUTO BIND TO CLASS OBJECT
	 * 
	 * @param S s
	 * @return E : is datatype of Object
	 */
	@Override
	public E get(S s) {
		Transaction tx = null;
		E e;
		try {
			tx = session.beginTransaction();
			e = (E)session.get(this.annotatedClass, (Serializable) s);
			tx.commit();
		} catch (HibernateException ex) {
			if (tx!=null) tx.rollback();
		    return null;
		}catch(Exception ex1)
		{
			return null;
		}
		return e;
	}
	
	/**
	 * Use to GET AN OBJECT BY UNIQUE ANOTHER KEY (NATURAL ID) FROM DATABASE AND AUTO BIND TO CLASS OBJECT
	 * 
	 * WHEN USE : IF U HAVE AN TABLE ACCOUNT WITH PRIMARY KEY IS AUTO INCREMENT VALUE 
	 * AND A UNIQUE COLUMN NAMED Account. I want to get the object by Account (not PK, just unique) 
	 * 
	 * 
	 * @param Object object : is a value that u want to get object by it in database
	 * 
	 * @return E : is datatype of Object
	 */
	@Override
	public E getBySimpleNaturalId(Object object) {
		Transaction tx = null;
		E e;
		try {
			tx = session.beginTransaction();
			e = (E)session.bySimpleNaturalId(this.annotatedClass).load((Serializable) object);
			tx.commit();
		} catch (HibernateException ex) {
			if (tx!=null) tx.rollback();
		    return null;
		}catch(Exception ex1)
		{
			return null;
		}
		return e;
	}
	
	/**
	 * Use to GET ALL OBJECT IN DATABASE AND BRING IT TO A LIST
	 * 
	 * @param void
	 * @return List<E> E : is entity object
	 */
	@Override
	public List<E> getAll() {
		Transaction tx = null;
		List<E> l;
		try {
			tx = session.beginTransaction();
			l = (List<E>)session.createCriteria(this.annotatedClass).list();
			tx.commit();
		} catch (HibernateException ex) {
			if (tx!=null) tx.rollback();
		    return null;
		}catch(Exception ex1)
		{
			return null;
		}
		return l;
	}
	
	/**
	 * Use to GET LIST OF DATA BY ROW INDEX FROM TO
	 * USE to paging
	 * 
	 * @param void
	 * @return List<E> E : is entity object
	 */
	@Override
	public List<E> getFromTo(int start, int max) {
		Transaction tx = null;
		List<E> l;
		try {
			tx = session.beginTransaction();
			Criteria crit = session.createCriteria(this.annotatedClass);
			crit.setFirstResult(start);
			crit.setMaxResults(max);
			l = (List<E>)crit.list();
			tx.commit();
		} catch (HibernateException ex) {
			if (tx!=null) tx.rollback();
		    return null;
		}catch(Exception ex1)
		{
			return null;
		}
		return l;
	}

	/**
	 * Use to execute a query using normal SQL to get a list
	 * 
	 * @param String s : sql string
	 * @return List<E> E : is entity object
	 */
	@Override
	public List<E> executeSQLQuery(String s) {
		Transaction tx = null;
		List<E> l = null;
		try {
			tx = session.beginTransaction();
			SQLQuery q = session.createSQLQuery(s);
			q.addEntity(this.annotatedClass);
			l = (List<E>)q.list();
			tx.commit();
		} catch (HibernateException ex) {
			if (tx!=null) tx.rollback();
		    	return null;
		}catch(Exception ex1)
		{
			return null;
		}
		return l;
	}
	
	/**
	 * LIKE executeSQLQuery(String s) but just get from row index start and match amount
	 * 
	 * @param String s : sql string
	 * @return List<E> E : is entity object
	 */
	@Override
	public List<E> executeSQLQuery(String s, int start, int amount) {
		Transaction tx = null;
		List<E> l = null;
		try {
			tx = session.beginTransaction();
			SQLQuery q = session.createSQLQuery(s);
			q.addEntity(this.annotatedClass);
			q.setFirstResult(start);
			q.setMaxResults(amount);
			l = (List<E>)q.list();
			tx.commit();
		} catch (HibernateException ex) {
			if (tx!=null) tx.rollback();
		    	return null;
		}catch(Exception ex1)
		{
			return null;
		}
		return l;
	}

	/**
	 * LIKE executeSQLQuery but s is HQL string
	 * 
	 * @param String s : HQL string
	 * @return List<E> E : is entity object
	 */
	@Override
	public List<E> executeHQLQuery(String s) {
		List<E> l = null;
		Transaction tx = null;
		try{
			tx = session.beginTransaction();
			Query q = session.createQuery(s);
			l = q.list();
			tx.commit();
		}catch(HibernateException ex) {
			if (tx!=null) tx.rollback();
	    	return null;
		}catch (Exception ex1) {
			return null;
		}
		return l;
	}

	/**
	 * DO NOT USE IT, MAYBE SOME BUG
	 */
	@Override
	public int executeSQLUpdate(String s) {
		Transaction tx = null;
		int res = 0;
		try{
			tx = session.beginTransaction();
			Query q = session.createQuery(s);
			res = q.executeUpdate();
			tx.commit();
		}catch(HibernateException ex) {
			if (tx!=null) tx.rollback();
	    	return 0;
		}catch (Exception ex1) {
			return 0;
		}
		return res;
	}
}
